Electronics
Wireless Headphones
Electronics
Smart Watch
Fashion
Running Shoes
Home
Coffee Maker
Fashion
Backpack
Electronics
Bluetooth Speaker
Sports
Yoga Mat
Home
Last Updated: December 19, 2025
Amazon is a large-scale e-commerce platform that enables users to browse, search, and purchase products from a wide range of categories, all from the convenience of a digital interface.
Electronics
Electronics
Fashion
Home
Fashion
Electronics
Sports
Home
Products are often sold by multiple sellers, with the platform acting as a central hub for managing listings, payments, logistics, and order fulfillment.
In this chapter, we will explore the low-level design of online shopping system like Amazon in detail.
Let's start by clarifying the requirements:
Before starting the design, it's important to ask thoughtful questions to uncover hidden assumptions and better define the scope of the system.
Here is an example of how a conversation between the candidate and the interviewer might unfold:
Candidate: Should the system support both buyers and sellers, or are we focusing only on the customer-facing side for now?
Interviewer: Let’s focus on the customer-facing side. You can assume that the product catalog is already populated by sellers.
Candidate: Should we support product categories, filters, and search functionality for browsing the catalog?
Interviewer: Yes, users should be able to search products by name and filter products by category,.
Candidate: Do we need to support features like cart management and order history?
Interviewer: Yes, customers should be able to add products to their cart, remove them, and view past orders.
Candidate: Should the system handle real-time inventory validation during checkout?
Interviewer: Yes. The system must check inventory before confirming an order and update stock levels immediately after a successful purchase.
Candidate: Are we implementing payment processing?
Interviewer: For this version, assume that payment is handled externally and is always successful. However, the system should be designed to support multiple payment strategies in the future.
Candidate: Should we allow users to leave reviews and ratings for products?
Interviewer: Not in this version. Let’s skip reviews and focus on browsing, ordering, and cart management.
After gathering the details, we can summarize the key system requirements.
Core entities are the fundamental building blocks of our system. We identify them by analyzing key nouns (e.g., user, product, cart, order, inventory) and actions (e.g., browse, search, add to cart, checkout, authenticate) from the functional requirements. These typically translate directly into classes, enums, or interfaces in an object-oriented design.
Let’s walk through the functional requirements and extract the relevant entities:
This points directly to a Product entity, which represents an item for sale. Products are classified, leading to a ProductCategory enum. The action of searching is encapsulated in a SearchService that operates on the product catalog.
The concept of a ShoppingCart is a core entity that holds items a customer intends to buy. Each entry in the cart is a CartItem, which links a Product to a specific quantity. The cart itself is associated with a user's Account.
The central entity for a purchase is the Order. It captures a transaction at a specific point in time and contains immutable OrderLineItem objects, which are snapshots of the products' details (name, price) at the time of purchase. An Order also has a lifecycle, which is managed by an OrderStatus enum (PLACED, SHIPPED, etc.).
The user of the system is a Customer. This entity holds personal information, an Account for managing credentials and the cart, and an Address for shipping.
To manage product availability, an InventoryService is crucial. This service is responsible for checking and updating stock levels transactionally to prevent overselling.
The need for flexible payment options suggests a PaymentStrategy interface, allowing different payment methods (e.g., CreditCardPaymentStrategy, UPIPaymentStrategy) to be used interchangeably. A PaymentService coordinates the execution of the chosen strategy.
To manage the interactions between all these components, an OnlineShoppingSystem class acts as a Facade and Singleton. It provides a simple entry point for clients to perform actions like registering customers, adding products to the cart, and placing orders.
Customer: Represents the user of the system. Holds personal details, an Account, and an Address.Product: Represents an item available for sale in the catalog.ShoppingCart: A temporary container for CartItem objects that a customer intends to purchase.CartItem: Represents a specific quantity of a Product within a ShoppingCart.Order: Represents a successful transaction with one or more ordered items.OrderLineItem: An immutable record of a product included in an Order, capturing its price and quantity at the time of purchase.Enums (OrderStatus, ProductCategory): Define fixed sets of constants for order statuses and product categories, ensuring consistency.Services (InventoryService, OrderService, PaymentService, SearchService): Encapsulate the core business logic for managing stock, creating orders, processing payments, and searching the product catalog.OnlineShoppingSystem: A Facade and Singleton that provides a unified, high-level interface to the entire e-commerce platform.These core entities define the essential abstractions of an online shopping system like Amazon and will guide the structure of your low-level design and class diagrams.
This section breaks down the system's architecture into its fundamental classes, their responsibilities, and the relationships that connect them. We also explore the key design patterns that provide robustness and flexibility to the solution.
The system is composed of several types of classes, each with a distinct role.
OrderStatus: Defines the discrete stages of an order's lifecycle, such as PLACED, SHIPPED, and DELIVERED.ProductCategory: Classifies products into fixed categories like ELECTRONICS, BOOKS, and CLOTHING.AddressA simple data class holding a customer's shipping location.
AccountEncapsulates a user's credentials and owns their ShoppingCart.
CartItemA data class that links a Product to a specific quantity within a shopping cart.
ShoppingCartA container for a collection of CartItems, responsible for managing items and calculating the total price.
OrderLineItemAn immutable snapshot of a product's details (ID, name, price, quantity) at the moment an order is placed. This prevents issues if product prices or names change later.
CustomerRepresents a user of the shopping system.
It acts as a concrete Observer, receiving status updates for its orders. It holds an Account and personal details.
Product (Abstract Class)The base class for all items sold on the platform.
Its construction is simplified by a nested Builder.
OrderThe central class representing a customer's purchase.
It acts as the Context for the State pattern (delegating actions to its currentState object) and as the Subject for the Observer pattern (notifying the Customer of status changes).
InventoryService, OrderService, PaymentService, SearchServiceService-layer classes that encapsulate specific business logic, such as managing stock, creating orders, processing payments, and searching the product catalog.
OnlineShoppingSystem (Singleton & Facade)The primary entry point for the application.
It orchestrates all the services and manages the main data stores (products, customers, orders). It provides a simplified interface to the client, hiding the complex interactions between the various components.
The relationships between classes define the system's structure and data flow.
Customer "has-an" Account. The Account's lifecycle is managed by the Customer.Account "has-a" ShoppingCart.ShoppingCart "has-a" collection of CartItems.Order "has-a" list of OrderLineItems.OnlineShoppingSystem "has-a" set of core services and manages the collections of all Products and Customers.Order is associated with one Customer.CartItem is associated with a Product.PaymentService is associated with a PaymentStrategy to process a payment.Order (Subject) is associated with its OrderObservers (the Customer).Order (Context) is associated with a single, current OrderState.ProductDecorator extends Product. GiftWrapDecorator extends ProductDecorator.Order extends the abstract Subject class.Customer implements the OrderObserver interface.OrderState classes implement the OrderState interface.PaymentStrategy classes implement the PaymentStrategy interface.OnlineShoppingSystem (Facade) depends on its various service classes to execute commands.OrderService depends on InventoryService to validate and update stock.Product.Builder to construct new Product objects.Order depends on OrderObserver to send notifications.The PaymentStrategy allows the algorithm for processing payments to be encapsulated and made interchangeable. The system can easily support new payment methods (e.g., PayPal, NetBanking) by creating new strategy classes without altering the PaymentService.
The lifecycle of an Order is managed using the State pattern. The Order (Context) delegates its behavior to different OrderState objects (PlacedState, ShippedState). This cleanly separates state-specific logic and makes managing transitions robust and easy to understand.
This pattern is fundamental for providing real-time updates. The Order (Subject) notifies the Customer (Observer) whenever its status changes. This decouples the core order processing logic from the notification mechanism.
The GiftWrapDecorator adds new functionality (extra cost and a modified description) to a Product object dynamically at runtime. This allows for flexible feature extension without creating a large number of subclasses.
The Product.Builder class is used for the step-by-step construction of a Product object. This is ideal for objects with many optional parameters (like description and category), providing a fluent API while ensuring the object is created in a valid state.
The OnlineShoppingSystem class acts as a facade. It provides a simple, high-level API (addProduct, placeOrder, addToCart) that hides the complex internal workflows involving multiple services, data models, and state management.
OnlineShoppingSystem is implemented as a singleton to ensure a single, globally accessible point of control for the entire application. This centralizes the management of services and data stores.
Defines standard constants for order lifecycle and product classification to ensure consistent use across the system.
1class OrderStatus(Enum):
2 PENDING_PAYMENT = "PENDING_PAYMENT"
3 PLACED = "PLACED"
4 SHIPPED = "SHIPPED"
5 DELIVERED = "DELIVERED"
6 CANCELLED = "CANCELLED"
7 RETURNED = "RETURNED"
8
9
10class ProductCategory(Enum):
11 ELECTRONICS = "ELECTRONICS"
12 BOOKS = "BOOKS"
13 CLOTHING = "CLOTHING"
14 HOME_GOODS = "HOME_GOODS"
15 GROCERY = "GROCERY"Represents a customer’s delivery location.
1class Address:
2 def __init__(self, street: str, city: str, state: str, zip_code: str):
3 self.street = street
4 self.city = city
5 self.state = state
6 self.zip_code = zip_code
7
8 def __str__(self) -> str:
9 return f"{self.street}, {self.city}, {self.state} {self.zip_code}"1class Customer(OrderObserver):
2 def __init__(self, name: str, email: str, password: str, shipping_address: Address):
3 self.id = str(uuid.uuid4())
4 self.name = name
5 self.email = email
6 self.account = Account(email, password)
7 self.shipping_address = shipping_address
8
9 def update(self, order: 'Order') -> None:
10 print(f"[Notification for {self.name}]: Your order #{order.get_id()} status has been updated to: {order.get_status().value}.")
11
12 def get_id(self) -> str:
13 return self.id
14
15 def get_name(self) -> str:
16 return self.name
17
18 def get_account(self) -> 'Account':
19 return self.account
20
21 def get_shipping_address(self) -> Address:
22 return self.shipping_address
23
24 def set_shipping_address(self, address: Address) -> None:
25 self.shipping_address = addressEach Customer has a personal cart and is notified about order status changes using the Observer Pattern.
1class Product(ABC):
2 def __init__(self):
3 self.id: str = ""
4 self.name: str = ""
5 self.description: str = ""
6 self.price: float = 0.0
7 self.category: ProductCategory = None
8
9 @abstractmethod
10 def get_id(self) -> str:
11 pass
12
13 @abstractmethod
14 def get_name(self) -> str:
15 pass
16
17 @abstractmethod
18 def get_description(self) -> str:
19 pass
20
21 @abstractmethod
22 def get_price(self) -> float:
23 pass
24
25 @abstractmethod
26 def get_category(self) -> ProductCategory:
27 pass
28
29 class BaseProduct:
30 def __init__(self, product_id: str, name: str, description: str, price: float, category: ProductCategory):
31 self.id = product_id
32 self.name = name
33 self.description = description
34 self.price = price
35 self.category = category
36
37 def get_id(self) -> str:
38 return self.id
39
40 def get_name(self) -> str:
41 return self.name
42
43 def get_description(self) -> str:
44 return self.description
45
46 def get_price(self) -> float:
47 return self.price
48
49 def get_category(self) -> ProductCategory:
50 return self.category
51
52 class Builder:
53 def __init__(self, name: str, price: float):
54 self.name = name
55 self.price = price
56 self.description = ""
57 self.category = None
58
59 def with_description(self, description: str) -> 'Product.Builder':
60 self.description = description
61 return self
62
63 def with_category(self, category: ProductCategory) -> 'Product.Builder':
64 self.category = category
65 return self
66
67 def build(self) -> 'Product':
68 return Product.BaseProduct(str(uuid.uuid4()), self.name, self.description, self.price, self.category)Uses the Builder Pattern to simplify product creation.
1class ProductDecorator(Product):
2 def __init__(self, decorated_product: Product):
3 super().__init__()
4 self.decorated_product = decorated_product
5
6 def get_id(self) -> str:
7 return self.decorated_product.get_id()
8
9 def get_name(self) -> str:
10 return self.decorated_product.get_name()
11
12 def get_price(self) -> float:
13 return self.decorated_product.get_price()
14
15 def get_description(self) -> str:
16 return self.decorated_product.get_description()
17
18 def get_category(self) -> ProductCategory:
19 return self.decorated_product.get_category()
20
21class GiftWrapDecorator(ProductDecorator):
22 GIFT_WRAP_COST = 5.00
23
24 def __init__(self, product: Product):
25 super().__init__(product)
26
27 def get_price(self) -> float:
28 return super().get_price() + self.GIFT_WRAP_COST
29
30 def get_description(self) -> str:
31 return super().get_description() + " (Gift Wrapped)"CartItem holds a product and quantity.
1class CartItem:
2 def __init__(self, product: Product, quantity: int):
3 self.product = product
4 self.quantity = quantity
5
6 def get_product(self) -> Product:
7 return self.product
8
9 def get_quantity(self) -> int:
10 return self.quantity
11
12 def increment_quantity(self, amount: int) -> None:
13 self.quantity += amount
14
15 def get_price(self) -> float:
16 return self.product.get_price() * self.quantityShoppingCart aggregates items and provides cart operations.
1class ShoppingCart:
2 def __init__(self):
3 self.items: Dict[str, CartItem] = {}
4
5 def add_item(self, product: Product, quantity: int) -> None:
6 if product.get_id() in self.items:
7 self.items[product.get_id()].increment_quantity(quantity)
8 else:
9 self.items[product.get_id()] = CartItem(product, quantity)
10
11 def remove_item(self, product_id: str) -> None:
12 if product_id in self.items:
13 del self.items[product_id]
14
15 def get_items(self) -> Dict[str, CartItem]:
16 return self.items.copy()
17
18 def calculate_total(self) -> float:
19 return sum(item.get_price() for item in self.items.values())
20
21 def clear_cart(self) -> None:
22 self.items.clear()Encapsulates a user’s credentials and their shopping cart.
1class Account:
2 def __init__(self, username: str, password: str):
3 self.username = username
4 self.password = password
5 self.cart = ShoppingCart()
6
7 def get_cart(self) -> ShoppingCart:
8 return self.cartRepresents immutable snapshots of a product at time of purchase.
1class OrderLineItem:
2 def __init__(self, product_id: str, product_name: str, quantity: int, price_at_purchase: float):
3 self.product_id = product_id
4 self.product_name = product_name
5 self.quantity = quantity
6 self.price_at_purchase = price_at_purchase
7
8 def get_product_id(self) -> str:
9 return self.product_id
10
11 def get_quantity(self) -> int:
12 return self.quantity1class Order(Subject):
2 def __init__(self, customer: Customer, items: List[OrderLineItem], shipping_address: Address, total_amount: float):
3 super().__init__()
4 self.id = str(uuid.uuid4())[:8]
5 self.customer = customer
6 self.items = items
7 self.shipping_address = shipping_address
8 self.total_amount = total_amount
9 self.order_date = datetime.now()
10 self.status = OrderStatus.PLACED
11 self.current_state = PlacedState()
12 self.add_observer(customer)
13
14 def ship_order(self) -> None:
15 self.current_state.ship(self)
16
17 def deliver_order(self) -> None:
18 self.current_state.deliver(self)
19
20 def cancel_order(self) -> None:
21 self.current_state.cancel(self)
22
23 def get_id(self) -> str:
24 return self.id
25
26 def get_status(self) -> OrderStatus:
27 return self.status
28
29 def set_state(self, state: OrderState) -> None:
30 self.current_state = state
31
32 def set_status(self, status: OrderStatus) -> None:
33 self.status = status
34 self.notify_observers(self)
35
36 def get_items(self) -> List[OrderLineItem]:
37 return self.items1class OutOfStockException(Exception):
2 def __init__(self, message: str):
3 super().__init__(message)1class OrderObserver(ABC):
2 @abstractmethod
3 def update(self, order: 'Order') -> None:
4 pass
5
6
7class Subject:
8 def __init__(self):
9 self.observers: List[OrderObserver] = []
10
11 def add_observer(self, observer: OrderObserver) -> None:
12 self.observers.append(observer)
13
14 def remove_observer(self, observer: OrderObserver) -> None:
15 if observer in self.observers:
16 self.observers.remove(observer)
17
18 def notify_observers(self, order: 'Order') -> None:
19 for observer in self.observers:
20 observer.update(order)Encapsulates state-specific behavior and transitions for orders using the State Pattern.
1class OrderState(ABC):
2 @abstractmethod
3 def ship(self, order: 'Order') -> None:
4 pass
5
6 @abstractmethod
7 def deliver(self, order: 'Order') -> None:
8 pass
9
10 @abstractmethod
11 def cancel(self, order: 'Order') -> None:
12 pass
13
14
15class PlacedState(OrderState):
16 def ship(self, order: 'Order') -> None:
17 print(f"Shipping order {order.get_id()}")
18 order.set_status(OrderStatus.SHIPPED)
19 order.set_state(ShippedState())
20
21 def deliver(self, order: 'Order') -> None:
22 print("Cannot deliver an order that has not been shipped.")
23
24 def cancel(self, order: 'Order') -> None:
25 print(f"Cancelling order {order.get_id()}")
26 order.set_status(OrderStatus.CANCELLED)
27 order.set_state(CancelledState())
28
29
30class ShippedState(OrderState):
31 def ship(self, order: 'Order') -> None:
32 print("Order is already shipped.")
33
34 def deliver(self, order: 'Order') -> None:
35 print(f"Delivering order {order.get_id()}")
36 order.set_status(OrderStatus.DELIVERED)
37 order.set_state(DeliveredState())
38
39 def cancel(self, order: 'Order') -> None:
40 print("Cannot cancel a shipped order.")
41
42
43class DeliveredState(OrderState):
44 def ship(self, order: 'Order') -> None:
45 print("Order already delivered.")
46
47 def deliver(self, order: 'Order') -> None:
48 print("Order already delivered.")
49
50 def cancel(self, order: 'Order') -> None:
51 print("Cannot cancel a delivered order.")
52
53
54class CancelledState(OrderState):
55 def ship(self, order: 'Order') -> None:
56 print("Cannot ship a cancelled order.")
57
58 def deliver(self, order: 'Order') -> None:
59 print("Cannot deliver a cancelled order.")
60
61 def cancel(self, order: 'Order') -> None:
62 print("Order is already cancelled.")Defines a flexible way to support multiple payment options using the Strategy Pattern.
1class PaymentStrategy(ABC):
2 @abstractmethod
3 def pay(self, amount: float) -> bool:
4 pass
5
6class UPIPaymentStrategy(PaymentStrategy):
7 def __init__(self, upi_id: str):
8 self.upi_id = upi_id
9
10 def pay(self, amount: float) -> bool:
11 print(f"Processing UPI payment of ${amount:.2f} with upi id {self.upi_id}.")
12 return True
13
14class CreditCardPaymentStrategy(PaymentStrategy):
15 def __init__(self, card_number: str):
16 self.card_number = card_number
17
18 def pay(self, amount: float) -> bool:
19 print(f"Processing credit card payment of ${amount:.2f} with card {self.card_number}.")
20 return TrueManages stock validation and deduction with thread safety.
1class InventoryService:
2 def __init__(self):
3 self.stock: Dict[str, int] = defaultdict(int)
4 self.lock = threading.Lock()
5
6 def add_stock(self, product: Product, quantity: int) -> None:
7 with self.lock:
8 self.stock[product.get_id()] += quantity
9
10 def update_stock_for_order(self, items: List[OrderLineItem]) -> None:
11 with self.lock:
12 # First, check if all items are in stock
13 for item in items:
14 if self.stock[item.get_product_id()] < item.get_quantity():
15 raise OutOfStockException(f"Not enough stock for product ID: {item.get_product_id()}")
16
17 # If all checks pass, deduct the stock
18 for item in items:
19 self.stock[item.get_product_id()] -= item.get_quantity()Coordinates inventory checks and order creation. Throws OutOfStockException if stock is insufficient.
1class OrderService:
2 def __init__(self, inventory_service: InventoryService):
3 self.inventory_service = inventory_service
4
5 def create_order(self, customer: Customer, cart: ShoppingCart) -> Order:
6 order_items = [
7 OrderLineItem(
8 cart_item.get_product().get_id(),
9 cart_item.get_product().get_name(),
10 cart_item.get_quantity(),
11 cart_item.get_product().get_price()
12 )
13 for cart_item in cart.get_items().values()
14 ]
15
16 self.inventory_service.update_stock_for_order(order_items)
17
18 return Order(customer, order_items, customer.get_shipping_address(), cart.calculate_total())Delegates payment processing to the selected strategy implementation.
1class PaymentService:
2 def process_payment(self, strategy: PaymentStrategy, amount: float) -> bool:
3 return strategy.pay(amount)Provides name- and category-based product search functionality.
1class SearchService:
2 def __init__(self, product_catalog: Collection[Product]):
3 self.product_catalog = product_catalog
4
5 def search_by_name(self, name: str) -> List[Product]:
6 return [p for p in self.product_catalog if name.lower() in p.get_name().lower()]
7
8 def search_by_category(self, category: ProductCategory) -> List[Product]:
9 return [p for p in self.product_catalog if p.get_category() == category]1class OnlineShoppingSystem:
2 _instance = None
3 _lock = threading.Lock()
4
5 def __new__(cls):
6 if cls._instance is None:
7 with cls._lock:
8 if cls._instance is None:
9 cls._instance = super().__new__(cls)
10 return cls._instance
11
12 def __init__(self):
13 if hasattr(self, 'initialized'):
14 return
15
16 self.products: Dict[str, Product] = {}
17 self.customers: Dict[str, Customer] = {}
18 self.orders: Dict[str, Order] = {}
19
20 self.inventory_service = InventoryService()
21 self.payment_service = PaymentService()
22 self.order_service = OrderService(self.inventory_service)
23 self.search_service = SearchService(self.products.values())
24
25 self.initialized = True
26
27 @classmethod
28 def get_instance(cls) -> 'OnlineShoppingSystem':
29 return cls()
30
31 def add_product(self, product: Product, initial_stock: int) -> None:
32 self.products[product.get_id()] = product
33 self.inventory_service.add_stock(product, initial_stock)
34
35 def register_customer(self, name: str, email: str, password: str, address: Address) -> Customer:
36 customer = Customer(name, email, password, address)
37 self.customers[customer.get_id()] = customer
38 return customer
39
40 def add_to_cart(self, customer_id: str, product_id: str, quantity: int) -> None:
41 customer = self.customers[customer_id]
42 product = self.products[product_id]
43 customer.get_account().get_cart().add_item(product, quantity)
44
45 def get_customer_cart(self, customer_id: str) -> ShoppingCart:
46 customer = self.customers[customer_id]
47 return customer.get_account().get_cart()
48
49 def search_products(self, name: str) -> List[Product]:
50 return self.search_service.search_by_name(name)
51
52 def place_order(self, customer_id: str, payment_strategy: PaymentStrategy) -> Optional[Order]:
53 customer = self.customers[customer_id]
54 cart = customer.get_account().get_cart()
55
56 if not cart.get_items():
57 print("Cannot place an order with an empty cart.")
58 return None
59
60 # 1. Process payment
61 payment_success = self.payment_service.process_payment(payment_strategy, cart.calculate_total())
62 if not payment_success:
63 print("Payment failed. Please try again.")
64 return None
65
66 # 2. Create order and update inventory
67 try:
68 order = self.order_service.create_order(customer, cart)
69 self.orders[order.get_id()] = order
70
71 # 3. Clear the cart
72 cart.clear_cart()
73
74 return order
75 except Exception as e:
76 print(f"Order placement failed: {e}")
77 return None1class OnlineShoppingDemo:
2 @staticmethod
3 def main():
4 # System Setup (Singleton and Services)
5 system = OnlineShoppingSystem.get_instance()
6
7 # Create and Add Products to Catalog (Builder Pattern)
8 laptop = Product.Builder("Dell XPS 15", 1499.99) \
9 .with_description("A powerful and sleek laptop.") \
10 .with_category(ProductCategory.ELECTRONICS) \
11 .build()
12
13 book = Product.Builder("The Pragmatic Programmer", 45.50) \
14 .with_description("A classic book for software developers.") \
15 .with_category(ProductCategory.BOOKS) \
16 .build()
17
18 system.add_product(laptop, 10) # 10 laptops in stock
19 system.add_product(book, 50) # 50 books in stock
20
21 # Register a Customer
22 alice_address = Address("123 Main St", "Anytown", "CA", "12345")
23 alice = system.register_customer("Alice", "[email protected]", "password123", alice_address)
24
25 # Alice Shops
26 print("--- Alice starts shopping ---")
27
28 # Alice adds a laptop to her cart
29 system.add_to_cart(alice.get_id(), laptop.get_id(), 1)
30 print("Alice added a laptop to her cart.")
31
32 # Alice decides to gift-wrap the book (Decorator Pattern)
33 gift_wrapped_book = GiftWrapDecorator(book)
34 system.add_to_cart(alice.get_id(), gift_wrapped_book.get_id(), 1)
35 print(f"Alice added a gift-wrapped book. Original price: ${book.get_price():.2f}, New price: ${gift_wrapped_book.get_price():.2f}")
36
37 alice_cart = system.get_customer_cart(alice.get_id())
38 print(f"Alice's cart total: ${alice_cart.calculate_total():.2f}")
39
40 # Alice Checks Out
41 print("\n--- Alice proceeds to checkout ---")
42 alice_order = system.place_order(alice.get_id(), CreditCardPaymentStrategy("1234-5678-9876-5432"))
43 if alice_order is None:
44 print("Order placement failed.")
45 return
46
47 print(f"Order #{alice_order.get_id()} placed successfully for Alice.")
48
49 # Order State and Notifications (State, Observer Patterns)
50 print("\n--- Order processing starts ---")
51
52 # The warehouse ships the order
53 alice_order.ship_order() # This will trigger a notification to Alice
54
55 # The delivery service marks the order as delivered
56 alice_order.deliver_order() # This will also trigger a notification
57
58 # Try to cancel a delivered order (State pattern prevents this)
59 alice_order.cancel_order()
60
61 print("\n--- Out of Stock Scenario ---")
62 bob = system.register_customer("Bob", "[email protected]", "pass123", alice_address)
63
64 # Bob tries to buy 15 laptops, but only 9 are left (1 was bought by Alice)
65 system.add_to_cart(bob.get_id(), laptop.get_id(), 15)
66
67 bob_order = system.place_order(bob.get_id(), UPIPaymentStrategy("testupi@hdfc"))
68 if bob_order is None:
69 print("Bob's order was correctly prevented due to insufficient stock.")
70
71if __name__ == "__main__":
72 OnlineShoppingDemo.main()Which entity is primarily responsible for managing the items a user intends to buy before placing an order in an online shopping system?
I didn't understand why did you complicated the Product class, till Builder class it makes sense where as BaseProduct is completley unnecessary and just making the design complex